/**
 * TODO Write Javadoc for ShakeListener
 * @author cathbar
 */
package com.dylankenny.flikrreader.android;

/* The following code was written by Matthew Wiggins
 * and is released under the APACHE 2.0 license
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 */

import android.content.Context;
import android.hardware.SensorListener;
import android.hardware.SensorManager;

public class ShakeListener implements SensorListener {
	private static final int FORCE_THRESHOLD = 350;
	private static final int TIME_THRESHOLD = 100;
	private static final int SHAKE_TIMEOUT = 500;
	private static final int SHAKE_DURATION = 1000;
	private static final int SHAKE_COUNT = 3;

	private SensorManager mSensorMgr;
	private float mLastX = -1.0f, mLastY = -1.0f, mLastZ = -1.0f;
	private long mLastTime;
	private OnShakeListener mShakeListener;
	private Context mContext;
	private int mShakeCount = 0;
	private long mLastShake;
	private long mLastForce;

	public interface OnShakeListener {
		public void onShake();
	}

	public ShakeListener(Context context) {
		this.mContext = context;
		this.resume();
	}

	public void setOnShakeListener(OnShakeListener listener) {
		this.mShakeListener = listener;
	}

	public void resume() {
		this.mSensorMgr = (SensorManager) this.mContext.getSystemService(Context.SENSOR_SERVICE);
		if (this.mSensorMgr == null) {
			throw new UnsupportedOperationException("Sensors not supported");
		}
		boolean supported = this.mSensorMgr.registerListener(this, SensorManager.SENSOR_ACCELEROMETER, SensorManager.SENSOR_DELAY_GAME);
		if (!supported) {
			this.mSensorMgr.unregisterListener(this, SensorManager.SENSOR_ACCELEROMETER);
			throw new UnsupportedOperationException("Accelerometer not supported");
		}
	}

	public void pause() {
		if (this.mSensorMgr != null) {
			this.mSensorMgr.unregisterListener(this, SensorManager.SENSOR_ACCELEROMETER);
			this.mSensorMgr = null;
		}
	}

	@Override
	public void onAccuracyChanged(int sensor, int accuracy) {
	}

	@Override
	public void onSensorChanged(int sensor, float[] values) {
		if (sensor != SensorManager.SENSOR_ACCELEROMETER)
			return;
		long now = System.currentTimeMillis();

		if ((now - this.mLastForce) > SHAKE_TIMEOUT) {
			this.mShakeCount = 0;
		}

		if ((now - this.mLastTime) > TIME_THRESHOLD) {
			long diff = now - this.mLastTime;
			float speed = Math.abs(values[SensorManager.DATA_X] + values[SensorManager.DATA_Y] + values[SensorManager.DATA_Z] - this.mLastX - this.mLastY
					- this.mLastZ)
					/ diff * 10000;
			if (speed > FORCE_THRESHOLD) {
				if ((++this.mShakeCount >= SHAKE_COUNT) && (now - this.mLastShake > SHAKE_DURATION)) {
					this.mLastShake = now;
					this.mShakeCount = 0;
					if (this.mShakeListener != null) {
						this.mShakeListener.onShake();
					}
				}
				this.mLastForce = now;
			}
			this.mLastTime = now;
			this.mLastX = values[SensorManager.DATA_X];
			this.mLastY = values[SensorManager.DATA_Y];
			this.mLastZ = values[SensorManager.DATA_Z];
		}
	}

}